﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.Shell;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;

namespace Gauss.Isp
{
    public class DebuggerToolWindowControl : UserControl
    {
        private TreeView _treeView;
        private TableLayoutPanel _tableLayoutPanel;
        private Button _btnNextTransition;
        private FlowLayoutPanel _flowLayoutPanel;
        private ComboBox _cmbBoxLockedRanks;
        private TreeNode _currentNode;
        private int _currentLockedRank;
        private Envelope _lastTransition;

        // The event handlers to communicate with the debugger thread.
        public event EventHandler NextTransitionClick;
        public event EventHandler<LockedRankChangedEventArgs> LockedRankChanged;
        public event EventHandler<TransitionClickedEventArgs> TransitionClicked;
    
        private void InitializeComponent()
        {
            this._treeView = new System.Windows.Forms.TreeView();
            this._tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
            this._flowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel();
            this._btnNextTransition = new System.Windows.Forms.Button();
            this._cmbBoxLockedRanks = new System.Windows.Forms.ComboBox();
            this._tableLayoutPanel.SuspendLayout();
            this._flowLayoutPanel.SuspendLayout();
            this.SuspendLayout();
            // 
            // _treeView
            // 
            this._treeView.Dock = System.Windows.Forms.DockStyle.Fill;
            this._treeView.Location = new System.Drawing.Point(3, 38);
            this._treeView.Name = "_treeView";
            this._treeView.Size = new System.Drawing.Size(405, 300);
            this._treeView.TabIndex = 0;
            this._treeView.DoubleClick += new System.EventHandler(this.TreeView_DoubleClicked);
            // 
            // _tableLayoutPanel
            // 
            this._tableLayoutPanel.ColumnCount = 1;
            this._tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this._tableLayoutPanel.Controls.Add(this._treeView, 0, 1);
            this._tableLayoutPanel.Controls.Add(this._flowLayoutPanel, 0, 0);
            this._tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
            this._tableLayoutPanel.Location = new System.Drawing.Point(0, 0);
            this._tableLayoutPanel.Name = "_tableLayoutPanel";
            this._tableLayoutPanel.RowCount = 2;
            this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 35F));
            this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this._tableLayoutPanel.Size = new System.Drawing.Size(411, 341);
            this._tableLayoutPanel.TabIndex = 1;
            // 
            // _flowLayoutPanel
            // 
            this._flowLayoutPanel.Anchor = System.Windows.Forms.AnchorStyles.None;
            this._flowLayoutPanel.AutoSize = true;
            this._flowLayoutPanel.Controls.Add(this._btnNextTransition);
            this._flowLayoutPanel.Controls.Add(this._cmbBoxLockedRanks);
            this._flowLayoutPanel.Location = new System.Drawing.Point(88, 3);
            this._flowLayoutPanel.Name = "_flowLayoutPanel";
            this._flowLayoutPanel.Size = new System.Drawing.Size(235, 29);
            this._flowLayoutPanel.TabIndex = 1;
            // 
            // _btnNextTransition
            // 
            this._btnNextTransition.Anchor = System.Windows.Forms.AnchorStyles.None;
            this._btnNextTransition.AutoSize = true;
            this._btnNextTransition.Location = new System.Drawing.Point(3, 3);
            this._btnNextTransition.Name = "_btnNextTransition";
            this._btnNextTransition.Size = new System.Drawing.Size(95, 23);
            this._btnNextTransition.TabIndex = 1;
            this._btnNextTransition.Text = "Next Transition";
            this._btnNextTransition.UseVisualStyleBackColor = true;
            this._btnNextTransition.Click += new System.EventHandler(this.NextTransition_Click);
            // 
            // _cmbBoxLockedRanks
            // 
            this._cmbBoxLockedRanks.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this._cmbBoxLockedRanks.FormattingEnabled = true;
            this._cmbBoxLockedRanks.Location = new System.Drawing.Point(111, 3);
            this._cmbBoxLockedRanks.Margin = new System.Windows.Forms.Padding(10, 3, 3, 3);
            this._cmbBoxLockedRanks.Name = "_cmbBoxLockedRanks";
            this._cmbBoxLockedRanks.Size = new System.Drawing.Size(121, 21);
            this._cmbBoxLockedRanks.TabIndex = 2;
            this._cmbBoxLockedRanks.SelectedIndexChanged += new System.EventHandler(this.LockedRanks_Changed);
            // 
            // DebuggerToolWindowControl
            // 
            this.Controls.Add(this._tableLayoutPanel);
            this.Name = "DebuggerToolWindowControl";
            this.Size = new System.Drawing.Size(411, 341);
            this._tableLayoutPanel.ResumeLayout(false);
            this._tableLayoutPanel.PerformLayout();
            this._flowLayoutPanel.ResumeLayout(false);
            this._flowLayoutPanel.PerformLayout();
            this.ResumeLayout(false);

        }

        public DebuggerToolWindowControl()
        {
            InitializeComponent();
        }

        public void SetInterleaving(IEnumerable<Envelope> interleaving, int processes)
        {
            // Construct the tree nodes.
            _lastTransition = null;
            TreeNode[] ranks = new TreeNode[processes];
            for (int i = 0; i < processes; i++)
            {
                ranks[i] = new TreeNode("Rank " + i);
                ranks[i].Expand();
            }
            int count = 0;
            foreach (Envelope e in interleaving)
            {
                TreeNode node = new TreeNode(Utils.StringFormat("{0}: {1}", count++, e.ToString()));
                if (e.IssueIndex < 0)
                    node.ForeColor = Color.Red;
                node.Tag = e;
                ranks[e.Rank].Nodes.Add(node);
                _lastTransition = e;
            }

            // Populate the tree.
            _currentNode = null;
            if (_treeView.InvokeRequired)
            {
                _treeView.Invoke(new MethodInvoker(
                    delegate()
                    {
                        _treeView.Nodes.Clear();
                        foreach (TreeNode n in ranks)
                            _treeView.Nodes.Add(n);
                    }));
            }
            else
            {
                _treeView.Nodes.Clear();
                foreach (TreeNode n in ranks)
                    _treeView.Nodes.Add(n);
            }

            // Populate the "Locked Ranks" dropdown correctly.
            _currentLockedRank = -1;
            if (_cmbBoxLockedRanks.InvokeRequired)
            {
                _cmbBoxLockedRanks.Invoke(new MethodInvoker(
                    delegate()
                    {
                        this._cmbBoxLockedRanks.SelectedIndexChanged -= new System.EventHandler(this.LockedRanks_Changed);
                        _cmbBoxLockedRanks.Items.Clear();
                        _cmbBoxLockedRanks.Items.Add("No Ranks Locked");
                        for (int i = 0; i < processes; i++)
                            _cmbBoxLockedRanks.Items.Add(i);
                        _cmbBoxLockedRanks.SelectedIndex = _currentLockedRank + 1;
                        this._cmbBoxLockedRanks.SelectedIndexChanged += new System.EventHandler(this.LockedRanks_Changed);
                    }));
            }
            else
            {
                this._cmbBoxLockedRanks.SelectedIndexChanged -= new System.EventHandler(this.LockedRanks_Changed);
                _cmbBoxLockedRanks.Items.Clear();
                _cmbBoxLockedRanks.Items.Add("No Ranks Locked");
                for (int i = 0; i < processes; i++)
                    _cmbBoxLockedRanks.Items.Add(i);
                _cmbBoxLockedRanks.SelectedIndex = _currentLockedRank + 1;
                this._cmbBoxLockedRanks.SelectedIndexChanged += new System.EventHandler(this.LockedRanks_Changed);
            }
        }

        public void SetRankToPid(Dictionary<int, int> rankToPid)
        {
            if (_cmbBoxLockedRanks.InvokeRequired)
            {
                _cmbBoxLockedRanks.Invoke(new MethodInvoker(
                    delegate()
                    {
                        for (int i = 0; i < _treeView.Nodes.Count; i++)
                        {
                            try
                            {
                                _treeView.Nodes[i].Text = "Rank " + i + " (pid " + rankToPid[i] + ")";
                            }
                            catch { }
                        }
                    }));
            }
            else
            {
                for (int i = 0; i < _treeView.Nodes.Count; i++)
                {
                    try
                    {
                        _treeView.Nodes[i].Text = "Rank " + i + " (pid " + rankToPid[i] + ")";
                    }
                    catch { }
                }
            }
        }

        public void SetTransition(Envelope transition)
        {
            if (_treeView.InvokeRequired)
            {
                _treeView.Invoke(new MethodInvoker(
                    delegate()
                    {
                        if (_currentNode != null)
                        {
                            _currentNode.ForeColor = Color.Gray;
                        }

                        TreeNode rankNode = _treeView.Nodes[transition.Rank];
                        foreach (TreeNode n in rankNode.Nodes)
                        {
                            if (n.Tag == transition)
                            {
                                //n.NodeFont = new Font(_treeView.Font, FontStyle.Bold);
                                n.ForeColor = Color.Blue;
                                _currentNode = n;
                                if (n.Tag as Envelope == _lastTransition)
                                    _btnNextTransition.Enabled = false;
                                else
                                    _btnNextTransition.Enabled = true;
                                break;
                            }
                        }
                    }));
            }
            else
            {
                if (_currentNode != null)
                {
                    _currentNode.ForeColor = Color.Gray;
                }

                TreeNode rankNode = _treeView.Nodes[transition.Rank];
                foreach (TreeNode n in rankNode.Nodes)
                {
                    if (n.Tag == transition)
                    {
                        //n.NodeFont = new Font(_treeView.Font, FontStyle.Bold);
                        n.ForeColor = Color.Blue;
                        _currentNode = n;
                        if (n.Tag as Envelope == _lastTransition)
                            _btnNextTransition.Enabled = false;
                        else
                            _btnNextTransition.Enabled = true;
                        break;
                    }
                }
            }
        }

        private void NextTransition_Click(object sender, EventArgs e)
        {
            if (NextTransitionClick != null)
                NextTransitionClick(sender, e);
        }

        private void LockedRanks_Changed(object sender, EventArgs e)
        {
            if (_cmbBoxLockedRanks.SelectedIndex - 1 != _currentLockedRank)
            {
                _currentLockedRank = _cmbBoxLockedRanks.SelectedIndex - 1;
                if (LockedRankChanged != null)
                    LockedRankChanged(sender, new LockedRankChangedEventArgs(_currentLockedRank));
            }
        }

        private void TreeView_DoubleClicked(object sender, EventArgs e)
        {
            TreeView tree = sender as TreeView;
            if (tree == null)
                return;

            Envelope env = tree.SelectedNode.Tag as Envelope;
            if (env == null)
                return;

            if (TransitionClicked != null)
                TransitionClicked(sender, new TransitionClickedEventArgs(env));
        }
    }

    public class LockedRankChangedEventArgs : EventArgs
    {
        private int _lockedRank;

        public int LockedRank
        {
            get { return _lockedRank; }
        }

        public LockedRankChangedEventArgs(int lockedRank)
        {
            _lockedRank = lockedRank;
        }
    }

    public class TransitionClickedEventArgs : EventArgs
    {
        private Envelope _clickedTransition;

        public Envelope Transition
        {
            get { return _clickedTransition; }
        }

        public TransitionClickedEventArgs(Envelope transition)
        {
            _clickedTransition = transition;
        }
    }

    [Guid("C4A760AB-B590-49c5-93B0-00EB5BC30405")]
    public class DebuggerToolWindow : ToolWindowPane
    {
        private DebuggerToolWindowControl _control;

        // The event handlers to communicate with the debugger thread.
        public event EventHandler NextTransitionClick;
        public event EventHandler<LockedRankChangedEventArgs> LockedRankChanged;
        public event EventHandler<TransitionClickedEventArgs> TransitionClicked;

        public DebuggerToolWindow()
            : base(null)
        {
            // Set the window title reading it from the resources.
            this.Caption = "ISP Debugger";

            // Set the image that will appear on the tab of the window frame
            // when docked with an other window
            // The resource ID correspond to the one defined in the resx file
            // while the Index is the offset in the bitmap strip. Each image in
            // the strip being 16x16.
            this.BitmapResourceID = 301;
            this.BitmapIndex = 0;
        }

        protected override void Initialize()
        {
            base.Initialize();

            _control = new DebuggerToolWindowControl();
            _control.NextTransitionClick += new EventHandler(btnNextTransition_Click);
            _control.TransitionClicked += new EventHandler<TransitionClickedEventArgs>(Transition_Clicked);
            _control.LockedRankChanged += new EventHandler<LockedRankChangedEventArgs>(LockedRanks_Changed);
        }

        private void btnNextTransition_Click(object sender, EventArgs e)
        {
            if (NextTransitionClick != null)
                NextTransitionClick(sender, e);
        }

        private void Transition_Clicked(object sender, TransitionClickedEventArgs e)
        {
            if (TransitionClicked != null)
                TransitionClicked(sender, e);
        }

        private void LockedRanks_Changed(object sender, LockedRankChangedEventArgs e)
        {
            if (LockedRankChanged != null)
                LockedRankChanged(sender, e);
        }

        public override IWin32Window Window
        {
            get { return (IWin32Window)_control; }
        }

        public void SetInterleaving(IEnumerable<Envelope> interleaving, int processes)
        {
            if (_control == null)
                return;

            // Invoking might be required, since on different thread.
            if (_control.InvokeRequired)
            {
                _control.Invoke(new MethodInvoker(
                    delegate()
                    {
                        _control.SetInterleaving(interleaving, processes);
                    }));
            }
            else
            {
                _control.SetInterleaving(interleaving, processes);
            }
        }

        public void SetRankToPid(Dictionary<int, int> rankToPid)
        {
            if (_control == null)
                return;

            // Invoking might be required, since on different thread.
            if (_control.InvokeRequired)
            {
                _control.Invoke(new MethodInvoker(
                    delegate()
                    {
                        _control.SetRankToPid(rankToPid);
                    }));
            }
            else
            {
                _control.SetRankToPid(rankToPid);
            }
        }

        public void SetTransition(Envelope transition)
        {
            if (_control == null)
                return;

            // Invoking might be required, since on different thread.
            if (_control.InvokeRequired)
            {
                _control.Invoke(new MethodInvoker(
                    delegate()
                    {
                        _control.SetTransition(transition);
                    }));
            }
            else
            {
                _control.SetTransition(transition);
            }
        }
    }
}
